package fr.xebia.monitoring.jdbc;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Map;

import fr.xebia.monitoring.Monitor;
import fr.xebia.monitoring.MonitorFactory;

public class MonitoredConnection implements Connection {

	private Connection innerConnection;
	private String rootMonitoringKey;
	private String monitoringKey;

	public MonitoredConnection(String key, Connection innerConnection) {
		super();
		this.innerConnection = innerConnection;
		this.rootMonitoringKey = key;
		this.monitoringKey = key+".connection";
	}

	public void clearWarnings() throws SQLException {
		innerConnection.clearWarnings();
	}

	public void close() throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "close()");
		try {
			innerConnection.close();
		} finally {
			monitor.stop();
		}
	}

	public void commit() throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "commit()");
		try {
			innerConnection.commit();
		} finally {
			monitor.stop();
		}
	}

	public Statement createStatement() throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "createStatement()");
		try {
			Statement statement = innerConnection.createStatement();
			return new MonitoredStatement(rootMonitoringKey, statement);
		} finally {
			monitor.stop();
		}
	}

	public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
			throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "createStatement()");
		try {
			Statement statement = innerConnection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
			return new MonitoredStatement(rootMonitoringKey, statement);
		} finally {
			monitor.stop();
		}
	}

	public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "createStatement()");
		try {
			Statement statement = innerConnection.createStatement(resultSetType, resultSetConcurrency);
			return new MonitoredStatement(rootMonitoringKey, statement);
		} finally {
			monitor.stop();
		}
	}

	public boolean getAutoCommit() throws SQLException {
		return innerConnection.getAutoCommit();
	}

	public String getCatalog() throws SQLException {
		return innerConnection.getCatalog();
	}

	public int getHoldability() throws SQLException {
		return innerConnection.getHoldability();
	}

	public DatabaseMetaData getMetaData() throws SQLException {
		return innerConnection.getMetaData();
	}

	public int getTransactionIsolation() throws SQLException {
		return innerConnection.getTransactionIsolation();
	}

	public Map<String, Class<?>> getTypeMap() throws SQLException {
		return innerConnection.getTypeMap();
	}

	public SQLWarning getWarnings() throws SQLException {
		return innerConnection.getWarnings();
	}

	public boolean isClosed() throws SQLException {
		return innerConnection.isClosed();
	}

	public boolean isReadOnly() throws SQLException {
		return innerConnection.isReadOnly();
	}

	public String nativeSQL(String sql) throws SQLException {
		return innerConnection.nativeSQL(sql);
	}

	public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareCall()");
		try {
			CallableStatement callableStatement = innerConnection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
			return new MonitoredCallableStatement(rootMonitoringKey, callableStatement);
		} finally {
			monitor.stop();
		}
	}

	public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareCall()");
		try {
			CallableStatement callableStatement = innerConnection.prepareCall(sql, resultSetType, resultSetConcurrency);
			return new MonitoredCallableStatement(rootMonitoringKey, callableStatement);
		} finally {
			monitor.stop();
		}
	}

	public CallableStatement prepareCall(String sql) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareCall()");
		try {
			CallableStatement callableStatement = innerConnection.prepareCall(sql);
			return new MonitoredCallableStatement(rootMonitoringKey, callableStatement);
		} finally {
			monitor.stop();
		}
	}

	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
			int resultSetHoldability) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareStatement()");
		try {
			PreparedStatement preparedStatement = innerConnection.prepareStatement(sql, resultSetType,
					resultSetConcurrency, resultSetHoldability);
			return new MonitoredPreparedStatement(rootMonitoringKey, preparedStatement);
		} finally {
			monitor.stop();
		}
	}

	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
			throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareStatement()");
		try {
			PreparedStatement preparedStatement = innerConnection.prepareStatement(sql, resultSetType,
					resultSetConcurrency);
			return new MonitoredPreparedStatement(rootMonitoringKey, preparedStatement);
		} finally {
			monitor.stop();
		}
	}

	public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareStatement()");
		try {
			PreparedStatement preparedStatement = innerConnection.prepareStatement(sql, autoGeneratedKeys);
			return new MonitoredPreparedStatement(rootMonitoringKey, preparedStatement);
		} finally {
			monitor.stop();
		}
	}

	public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareStatement()");
		try {
			PreparedStatement preparedStatement = innerConnection.prepareStatement(sql, columnIndexes);
			return new MonitoredPreparedStatement(rootMonitoringKey, preparedStatement);
		} finally {
			monitor.stop();
		}
	}

	public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareStatement()");
		try {
			PreparedStatement preparedStatement = innerConnection.prepareStatement(sql, columnNames);
			return new MonitoredPreparedStatement(rootMonitoringKey, preparedStatement);
		} finally {
			monitor.stop();
		}
	}

	public PreparedStatement prepareStatement(String sql) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "prepareStatement()");
		try {
			PreparedStatement preparedStatement = innerConnection.prepareStatement(sql);
			return new MonitoredPreparedStatement(rootMonitoringKey, preparedStatement);
		} finally {
			monitor.stop();
		}
	}

	public void releaseSavepoint(Savepoint savepoint) throws SQLException {
		innerConnection.releaseSavepoint(savepoint);
	}

	public void rollback() throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "rollback()");
		try {
			innerConnection.rollback();
		} finally {
			monitor.stop();
		}
	}

	public void rollback(Savepoint savepoint) throws SQLException {
		Monitor monitor = MonitorFactory.start(monitoringKey, "rollback()");
		try {
			innerConnection.rollback(savepoint);
		} finally {
			monitor.stop();
		}
	}

	public void setAutoCommit(boolean autoCommit) throws SQLException {
		innerConnection.setAutoCommit(autoCommit);
	}

	public void setCatalog(String catalog) throws SQLException {
		innerConnection.setCatalog(catalog);
	}

	public void setHoldability(int holdability) throws SQLException {
		innerConnection.setHoldability(holdability);
	}

	public void setReadOnly(boolean readOnly) throws SQLException {
		innerConnection.setReadOnly(readOnly);
	}

	public Savepoint setSavepoint() throws SQLException {
		return innerConnection.setSavepoint();
	}

	public Savepoint setSavepoint(String name) throws SQLException {
		return innerConnection.setSavepoint(name);
	}

	public void setTransactionIsolation(int level) throws SQLException {
		innerConnection.setTransactionIsolation(level);
	}

	public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
		innerConnection.setTypeMap(map);
	}
}
